/* fileHandler.c */

#include <direct.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include "fileHandler.h"
#include "configFile.h"
#include "httpConnectionHandler.h"

/* Defines */
#define WWW_FOLDER "/www/"
#define DEFAULT_ROOT_FILE "index.html"
#define FILE_BUFFER_SIZE 256

/* Prototypes */
static int getModifiedDate(fileInfoT* fileInfo, char* requestedFile);
static char* getFilePath(char *requestedFile);
static int readFile(fileInfoT *fileInfo, char* requestedFile, char* mimeType);
static char* getFileExtension(char *fileName);
static char* getFileMimeType(char *fileExtension);
static void expandFileBuffer(fileInfoT *fileInfo);

/* Function: readLogFile
* Usage: result = readLogFile(&fileInfo);
* -------------------------------- 
*  A function that gets the information
* stored in the logfile.
*/

int readLogFile(fileInfoT *fileInfo) {
	int result;

	result = readFile(fileInfo, getLogFilePath(), NULL);
	return result;
}

/* Function: getHttpContents
* Usage: result = getHttpContents(&fileInfo, conn);
* -------------------------------- 
*  A function that returns the content of a file.
*/

int getHttpContents(fileInfoT *fileInfo, httpConnectionT *conn) {
	fileInfoT modifiedFileDate;
	int result;
	if (!strcmp(conn->requestedFile, "/"))
		strcpy(conn->requestedFile, DEFAULT_ROOT_FILE);

	if (!strcmp(conn->getConditionalDate, "NONE")) {
		return getFileContents(fileInfo, conn->requestedFile);
	}
	else {
		getModifiedDate(&modifiedFileDate, getFilePath(conn->requestedFile));
		result = compareDates(conn->getConditionalDate, modifiedFileDate.lastModified);
		
		if (result == 1){
			return NO_CHANGE;
		}
		else {
			return getFileContents(fileInfo, conn->requestedFile);
		}
	}
}

/* Function: compareDates
* Usage: result = compareDates(conn->getConditionalDate, modifiedFileDate.lastModified);
* -------------------------------- 
* A function that looks at the date of the file and checks if it has been modified 
* after it was loaded.
*/


static int compareDates(char* date1, char* date2) {
	if (!strcmp(date1, date2))
		return 1;
	else
		return 0;
}

/* Function: getFileContents
* Usage:  result = getFileContents(&fileInfo, conn->requestedFile);
* -------------------------------- 
* Collects information about a requested file.
*/

int getFileContents(fileInfoT *fileInfo, char* requestedFile) {
	char *filePath, *fileContent, *fileExt, *fileType;
	int result;

	filePath = getFilePath(requestedFile);
	fileExt = getFileExtension(requestedFile);
	fileType = getFileMimeType(fileExt);
	
	result = readFile(fileInfo, filePath, fileType);
	strcpy(fileInfo->contentType, fileType);
	
	free(filePath);
	return result;
}

/* Function: getFileMimeType
* Usage: fileType = getFileMimeType(fileExt);
* -------------------------------- 
* A function that checks for what ending a file has and
* returns the correct mime type. 
*/

static char* getFileMimeType(char *fileExtension) {
	if(!strcmp(fileExtension, "html")) return "text/html";
	if(!strcmp(fileExtension, "gif")) return "image/gif";
	if(!strcmp(fileExtension, "png")) return "image/png";
	if(!strcmp(fileExtension, "jpeg")) return "image/jpeg";
	if(!strcmp(fileExtension, "jpg")) return "image/jpeg";
	if(!strcmp(fileExtension, "zip")) return "application/x-zip-compressed";
	if(!strcmp(fileExtension, "ico")) return "image/x-icon";
	else {
		return "text/html";
	}
}

/* Function: getFileExtension
* Usage: fileExt = getFileExtension(requestedFile);
* -------------------------------- 
* A function that looks for the extension in a filename.
*/

static char* getFileExtension(char *fileName) {
	char *dot = strrchr(fileName, '.');
    
	if(!dot || dot == fileName) return "";
    
	return dot + 1;
}

/* Function: getFilePath
* Usage: filePath = getFilePath(requestedFile);
* -------------------------------- 
* A function that collects information about where
* a file is located and returns the path.
*/

static char* getFilePath(char *requestedFile) {
	char* buffer, *fullPath;
	buffer = NULL;
	
	fullPath = (char*) malloc(512 * sizeof(char));
	
	buffer = _getcwd(NULL, 0);
	if (buffer == NULL)
		printf("_getcwd ERROR");
	
	strcpy(fullPath, buffer);
	strcat(fullPath, getWwwPath());
	strcat(fullPath, requestedFile);

	free(buffer);
	return fullPath;
}

/* Function: readFile
* Usage: result = readFile(fileInfo, getLogFilePath(), NULL);
* -------------------------------- 
* A function that checks if the requested file exists
* and reads information about it.
*/

static int readFile(fileInfoT *fileInfo, char* requestedFile, char* mimeType) {
	int i = 0, fileLen;
	char *type, ch;
	FILE *file; 
	
	if (mimeType == NULL)
		type = 1;
	else
		type = strstr(mimeType, "text");
	
	if(type != NULL) {
		fopen_s(&file, requestedFile, "r");

		if(file == NULL) 
			return FILE_NOT_FOUND;

		fileInfo->fileBufferSize = 0;
		fileInfo->fileContent = NULL;
		while((ch = fgetc(file) )!= EOF) {
			if (i >= fileInfo->fileBufferSize)
				expandFileBuffer(fileInfo);
			fileInfo->fileContent[i] = ch;
			i++;
		}

		if (fileInfo->fileContent != NULL) {
			fileInfo->contentSize = i;
			fileInfo->fileContent[i] = 0;
		}
	}
	else {
		fopen_s(&file, requestedFile, "rb");

		if(file == NULL) 
			return FILE_NOT_FOUND;

		fseek(file, 0, SEEK_END);
		fileLen = ftell(file);
		fseek(file, 0, SEEK_SET);

		fileInfo->fileBufferSize = 0;
		fileInfo->fileContent = NULL;
		fileInfo->fileContent = (char*)malloc(fileLen * sizeof(char));
		fileInfo->contentSize = fileLen;
		fread(fileInfo->fileContent, fileLen, sizeof(char), file);
	}
		
	/*
	fileInfo->fileBufferSize = 0;
	fileInfo->fileContent = NULL;
	while((ch = fgetc(file) )!= EOF) {
		if (i >= fileInfo->fileBufferSize)
			expandFileBuffer(fileInfo);
		fileInfo->fileContent[i] = ch;
		i++;
	}
	*/

	getModifiedDate(fileInfo, requestedFile);
	fclose(file);

	return RESULT_OK;
}

/* Function: expandFileBuffer
* Usage: expandFileBuffer(fileInfo);
* -------------------------------- 
* allocates memory depending on the 
* size of the size of the file. 
*/

static void expandFileBuffer(fileInfoT *fileInfo) {
	int newSize = fileInfo->fileBufferSize + FILE_BUFFER_SIZE;

	fileInfo->fileContent = (char *) realloc(fileInfo->fileContent, newSize * sizeof(char));
	fileInfo->fileBufferSize = newSize;
}

/* Function: getModifiedDate 
* Usage: getModifiedDate(fileInfo, requestedFile);
* -------------------------------- 
* Checks for the last time the requested file
* was updated.
*/

static int getModifiedDate(fileInfoT *fileInfo, char* requestedFile) {
	int result;
	struct _stat buffer;
	struct tm* timeinfo;
	errno_t err;

	result = _stat(requestedFile, &buffer);
	if(result)
		return STAT_ERROR;
	
	timeinfo = localtime(&buffer.st_mtime);
	err = formatDate(fileInfo->lastModified, timeinfo);
	
	if(err == 0)
		return CTIME_ERROR;

	return RESULT_OK;
}